
class ifu2isu_driver extends uvm_driver #(tr_ifu2isu);
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
    
  `uvm_component_utils_begin(ifu2isu_driver)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
    inf.ifu2isu <= ifu2isu_def;
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    time runDelay = 0ns;
    tr_ifu2isu req, rsp;
    if(!uvm_config_db#(time)::get(this, "", "runDelay", runDelay))
      `uvm_warning("NOVIF", {"delay time not set", get_full_name(), ".runDelay"});
    #runDelay;    
    forever begin
      @(posedge sysif.clk);
      seq_item_port.get_next_item(req);
      `uvm_info("isu drv", $psprintf("trans from ifu:\n%s", req.sprint()), UVM_FULL)
    	inf.ifu2isu <= tr2s_ifu2isu(req);
      seq_item_port.item_done();
    end
  endtask : run_phase
endclass : ifu2isu_driver

class dpu2isu_driver extends uvm_driver #(tr_dpu2isu);
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
    
  `uvm_component_utils_begin(dpu2isu_driver)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
      
    inf.dpu2isu <= dpu2isu_def;
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    time runDelay = 0ns;
    tr_dpu2isu req, rsp;
    if(!uvm_config_db#(time)::get(this, "", "runDelay", runDelay))
      `uvm_warning("NOVIF", {"delay time not set", get_full_name(), ".runDelay"});
    #runDelay;    
    forever begin
      @(posedge sysif.clk);
      seq_item_port.get_next_item(req);
      `uvm_info("isu drv", $psprintf("trans from ifu:\n%s", req.sprint()), UVM_FULL)
    	inf.dpu2isu <= tr2s_dpu2isu(req);
      seq_item_port.item_done();
    end
  endtask : run_phase
endclass : dpu2isu_driver

class lsu2isu_driver extends uvm_driver #(tr_lsu2isu);
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
    
  `uvm_component_utils_begin(lsu2isu_driver)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
      
    inf.lsu2isu <= lsu2isu_def;
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    time runDelay = 0ns;
    tr_lsu2isu req, rsp;
    if(!uvm_config_db#(time)::get(this, "", "runDelay", runDelay))
      `uvm_warning("NOVIF", {"delay time not set", get_full_name(), ".runDelay"});
    #runDelay;    
    forever begin
      @(posedge sysif.clk);
      seq_item_port.get_next_item(req);
      `uvm_info("isu drv", $psprintf("trans from ifu:\n%s", req.sprint()), UVM_FULL)
    	inf.lsu2isu <= tr2s_lsu2isu(req);
      seq_item_port.item_done();
    end
  endtask : run_phase
endclass : lsu2isu_driver

class exu2isu_driver extends uvm_driver #(tr_exu2isu);
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
    
  `uvm_component_utils_begin(exu2isu_driver)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
      
    inf.exu2isu <= exu2isu_def;
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    time runDelay = 0ns;
    tr_exu2isu req, rsp;
    if(!uvm_config_db#(time)::get(this, "", "runDelay", runDelay))
      `uvm_warning("NOVIF", {"delay time not set", get_full_name(), ".runDelay"});
    #runDelay;    
    forever begin
      @(posedge sysif.clk);
      seq_item_port.get_next_item(req);
      `uvm_info("isu drv", $psprintf("trans from ifu:\n%s", req.sprint()), UVM_FULL)
    	inf.exu2isu <= tr2s_exu2isu(req);
      seq_item_port.item_done();
    end
  endtask : run_phase
endclass : exu2isu_driver

class isu_monitor extends uvm_monitor;
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
	event cov_transaction;
  
	uvm_analysis_port #(tr_isu2ifu) ifu_port;
	uvm_analysis_port #(tr_isu2rfu) rfu_port;
	uvm_analysis_port #(tr_isu2dpu) dpu_port;
  uvm_analysis_port #(tr_isu2lsu) lsu_port;
  uvm_analysis_port #(tr_isu2exu) exu_port;
  
  `uvm_component_utils_begin(isu_monitor)
  `uvm_component_utils_end
  
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    ifu_port = new("ifu_port", this);
    rfu_port = new("rfu_port", this);
    dpu_port = new("dpu_port", this);
    lsu_port = new("lsu_port", this);
    exu_port = new("exu_port", this);
    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    forever begin
      @(posedge sysif.clk)
        get_trans;
    end
  endtask : run_phase
  
  task get_trans();
  	bit en = 0;
  	foreach(inf.cb.isu2ifu.req[i])
    	en |= inf.cb.isu2ifu.req[i];
    
  	if(en) begin
    	tr_isu2ifu tr = tr_isu2ifu::type_id::create("isu2ifu", this);
    	tr.pc = inf.cb.isu2ifu.pc;
    	tr.br = inf.cb.isu2ifu.br;
    	tr.brLid = inf.cb.isu2ifu.brLid;
    	tr.brLtid = inf.cb.isu2ifu.brLtid;
    	foreach(tr.req[i])
      	tr.req[i] = inf.cb.isu2ifu.req[i];
     	foreach(tr.ltid[i])
      	tr.ltid[i] = inf.cb.isu2ifu.ltid[i];        
      `uvm_info("isu mon", $psprintf("trans to ifu:\n%s", tr.sprint()), UVM_FULL)
    	ifu_port.write(tr);
  	end
    
  	en = 0;
  	foreach(inf.cb.isu2rfu.fuPts[i])
    	en |= inf.cb.isu2rfu.fuPts[i] != ptsn;
  	en |= inf.cb.isu2rfu.lsPts != ptsn;
  	foreach(inf.cb.isu2rfu.vwr[i, j])
    	en |= inf.cb.isu2rfu.vwr[i][j] == '1;
  	foreach(inf.cb.isu2rfu.swr[i, j])
    	en |= inf.cb.isu2rfu.swr[i][j] == '1;
                     
    if(en) begin
      tr_isu2rfu tr = tr_isu2rfu::type_id::create("isu2rfu", this);
      tr.start = inf.cb.isu2rfu.req;
      foreach(tr.swadr[i]) begin
        tr.swadr[i] = inf.cb.isu2rfu.swadr[i];
        tr.swLid[i] = inf.cb.isu2rfu.swLid[i];
        tr.swr[i] = inf.cb.isu2rfu.swr[i];
      end
    	foreach(tr.vradr[i, j])
      	tr.vradr[i][j] = inf.cb.isu2rfu.vradr[i][j];
    	foreach(tr.vwadr[i, j])
      	tr.vwadr[i][j] = inf.cb.isu2rfu.vwadr[i][j];
    	foreach(tr.sadr[i, j])
      	tr.sadr[i][j] = inf.cb.isu2rfu.sadr[i][j];
    	foreach(tr.vwr[i, j])
      	tr.vwr[i][j] = inf.cb.isu2rfu.vwr[i][j];
    	foreach(tr.fuOps[i]) begin
      	tr.fuOps[i] = isu_vopsel_t'(inf.cb.isu2rfu.fuRrf[i].ops);
      	tr.fuBks[i] = inf.cb.isu2rfu.fuRrf[i].bks;
      	tr.fuVec[i] = inf.cb.isu2rfu.fuRrf[i].vec;
      	tr.fuPts[i] = isu_vptsel_t'(inf.cb.isu2rfu.fuPts[i]);
    	end
        
    	tr.lsOps = isu_vopsel_t'(inf.cb.isu2rfu.lsRrf.ops);
    	tr.lsPts = isu_vptsel_t'(inf.cb.isu2rfu.lsPts);
    	foreach(tr.fuWSel[i, j])
      	tr.fuWSel[i][j] = inf.cb.isu2rfu.fuWSel[i][j];
    	foreach(tr.spAdr[i])
      	tr.spAdr[i] = inf.cb.isu2rfu.spAdr[i];
    	tr.spLid = inf.cb.isu2rfu.spLid;
    	tr.spLtid = inf.cb.isu2rfu.spLtid;
    	foreach(tr.spOps[i])
      	tr.spOps[i] = isu_vopsel_t'(inf.cb.isu2rfu.spOps[i]);
      foreach(tr.fuImm[i])
        tr.fuImm[i] = {`TBE(inf.cb.isu2rfu.fuImm[i]), inf.cb.isu2rfu.fuImm[i]};
      tr.lsImm = {`TBE(inf.cb.isu2rfu.lsImm), inf.cb.isu2rfu.lsImm};
      tr.spImm = {`TBE(inf.cb.isu2rfu.spImm), inf.cb.isu2rfu.spImm};
      tr.spPC = inf.cb.isu2rfu.spPC;
      tr.exFu = inf.cb.isu2rfu.exFu;
      tr.lsLid = inf.cb.isu2rfu.lsLid;
      foreach(tr.fuLid[i])
        tr.fuLid[i] = inf.cb.isu2rfu.fuLid[i];
      `uvm_info("isu mon", $psprintf("trans to rfu:\n%s", tr.sprint()), UVM_FULL)
    	rfu_port.write(tr);
    end
    
    en = 0;
    en = inf.cb.isu2lsu.req;
    en |= inf.cb.isu2lsu.rsp;
    
    if(en) begin
///      tr_isu2lsu tr = tr_isu2lsu::type_id::create("isu2lsu", this);;
///      tr.sRsp = inf.cb.isu2lsu.sRsp;
///      
///      `uvm_info("isu mon", $psprintf("trans to lsu:\n%s", tr.sprint()), UVM_FULL)
///    	ifu_port.write(tr);      
    end
  endtask
endclass : isu_monitor

/*
class ifu2isu_sequencer extends uvm_sequencer #(tr_ifu2isu);
  `uvm_component_utils(ifu2isu_sequencer)
  
  function new(string name, uvm_component parent=null);
    super.new(name, parent);
///    `uvm_update_sequence_lib_and_item(tr_ifu2isu)
  endfunction
  
endclass : ifu2isu_sequencer
*/

typedef uvm_sequencer#(tr_ifu2isu) ifu2isu_sequencer;
typedef uvm_sequencer#(tr_dpu2isu) dpu2isu_sequencer;
typedef uvm_sequencer#(tr_lsu2isu) lsu2isu_sequencer;
typedef uvm_sequencer#(tr_exu2isu) exu2isu_sequencer;

class isu_agent extends uvm_agent;
  uvm_active_passive_enum is_active = UVM_ACTIVE;
  `uvm_component_utils_begin(isu_agent)
    `uvm_field_enum(uvm_active_passive_enum, is_active, UVM_ALL_ON)
  `uvm_component_utils_end
  
  ifu2isu_sequencer ifu_sequencer;
  dpu2isu_sequencer dpu_sequencer;
  lsu2isu_sequencer lsu_sequencer;
  exu2isu_sequencer exu_sequencer;
  
  ifu2isu_driver ifu_driver;
  dpu2isu_driver dpu_driver;
  lsu2isu_driver lsu_driver;
  exu2isu_driver exu_driver;
  
  isu_monitor monitor;
  
  function new(string name, uvm_component parent=null);
    super.new(name, parent);
  endfunction /// new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    monitor = isu_monitor::type_id::create("monitor",this);
    if (is_active == UVM_ACTIVE) begin
      ifu_sequencer = ifu2isu_sequencer::type_id::create("ifu_sequencer",this);
      dpu_sequencer = dpu2isu_sequencer::type_id::create("dpu_sequencer",this);
      lsu_sequencer = lsu2isu_sequencer::type_id::create("lsu_sequencer",this);
      exu_sequencer = exu2isu_sequencer::type_id::create("exu_sequencer",this);
      
      dpu_driver = dpu2isu_driver::type_id::create("dpu_driver",this);
      ifu_driver = ifu2isu_driver::type_id::create("ifu_driver",this);
      lsu_driver = lsu2isu_driver::type_id::create("lsu_driver",this);
      exu_driver = exu2isu_driver::type_id::create("exu_driver",this);
    end
  endfunction : build_phase
  
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    if(is_active == UVM_ACTIVE) begin
      ifu_driver.seq_item_port.connect(ifu_sequencer.seq_item_export);
      dpu_driver.seq_item_port.connect(dpu_sequencer.seq_item_export);
      lsu_driver.seq_item_port.connect(lsu_sequencer.seq_item_export);
      exu_driver.seq_item_port.connect(exu_sequencer.seq_item_export);
    end
  endfunction : connect_phase
endclass : isu_agent
